/*
 * Copyright (C) 2008-2011 University of Deusto
 * 
 * All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.
 * 
 * This software consists of contributions made by many individuals, 
 * listed below:
 *
 * Author: Aitor Gómez Goiri <aitor.gomez@deusto.es>
 */
package otso.se.network.sockets.communication;

import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.restlet.data.Status;

import otso.se.network.sockets.communication.abstraction.HttpRequest;
import otso.se.network.sockets.communication.abstraction.HttpResponse;
import otso.se.network.sockets.communication.abstraction.ISocketApplication;
import otso.se.network.sockets.communication.abstraction.tcp.IUpcomingHttpConnectionListener;
import otso.se.network.sockets.communication.abstraction.tcp.TCPServer;
import otsopack.commons.IController;

public class OtsoSocketServer implements IUpcomingHttpConnectionListener {
	public static final int DEFAULT_PORT = 8182;
	
	private final Map<String,ISocketApplication> applications = new TreeMap<String,ISocketApplication>(new StringSizeComparator(true));
	private final TCPServer server;
	
	protected OtsoSocketServer() {
		this.server = new TCPServer(DEFAULT_PORT);
		this.server.addConnectionListener(this);
	}
	
	public OtsoSocketServer(IController controller) {
		this(DEFAULT_PORT, controller);
	}
	
	public OtsoSocketServer(int port, IController controller) {
		this.server = new TCPServer(port);
		this.server.addConnectionListener(this);
	    //this.component.getServers().add(Protocol.HTTP, this.port);
	    
		final OtsopackSocketApplication app = new OtsopackSocketApplication();
		app.setController(controller);
	    attach("/", app);
	}
	
	protected void attach(String appPath, ISocketApplication app) {
		this.applications.put(appPath, app);
	}
	
	public OtsopackSocketApplication getApplication(){
		return (OtsopackSocketApplication) this.applications.get("/");
	}
	
	public void startup() throws Exception {
		final Thread th = new Thread(new Runnable() {
			@Override
			public void run() {
				server.start();
			}
		});
		th.setDaemon(true);
		th.start();
	}
	
	public void shutdown() throws Exception {
		this.server.stop();
	}
	
	/**
	 * @return
	 * 		starting from the longest patterns, the first which matches the pattern
	 * 		is the one whose application will be returned. Null is returned if no
	 * 		matching pattern is found.
	 */
	protected ISocketApplication getMostSuitableApplication(String url) {
		for(Entry<String, ISocketApplication> app: this.applications.entrySet()) {
			if( url.startsWith(app.getKey()) ) {
				return app.getValue();
			}
		}
		return null;
	}
	
	@Override
	public HttpResponse handle(HttpRequest request) {
		final ISocketApplication app = getMostSuitableApplication(request.getUrl());
		if( app==null )
			return new HttpResponse(Status.CLIENT_ERROR_NOT_FOUND, System.currentTimeMillis());
		return app.dispatch(request);
	}
}

class StringSizeComparator implements Comparator<String> {
	boolean longerToShorter;
	
	StringSizeComparator(boolean longerToShorter) {
		this.longerToShorter = longerToShorter;
	}
	
	/**
	 * @return
	 * 		a negative integer, zero, or a positive integer as
	 * 		the first argument is less than, equal to, or greater
	 * 		than the second.
	 */
	@Override
	public int compare(String o1, String o2) {
		final int normal = o1.compareTo(o2);
		if( this.longerToShorter )
			return -1 * normal;
		return normal;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (this.longerToShorter ? 1231 : 1237);
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		StringSizeComparator other = (StringSizeComparator) obj;
		if (this.longerToShorter != other.longerToShorter)
			return false;
		return true;
	}
}